const { connectMongoDB, closeMongoDB } = require('./util')
const Neo4j = require('node-neo4j')

// 查询节点列表
const getRelationCountByNodeType = ({ url }, relationType) => {
  return new Promise(resolve => {
    const neo = new Neo4j(url)
    const cql = `MATCH ()-[R:${relationType}]->() RETURN COUNT(R) AS TOTAL_R`
    neo.cypherQuery(cql, (err, res) => {
      if (err) throw err
      const data = res.data[0]
      resolve(data)
    })
  })
}

// 分页查询
const getNodeListByPage = ({ url }, page, records, keywords, type) => {
  // 查询满足条件的节点总数
  const getNodeCount = (neo, keywords, type) => {
    return new Promise(resolve => {
      let cql = `MATCH (N${type === 'all' ? '' : ':'+type}) ${keywords === '' ? '' : 'WHERE N.name =~ ".*'+keywords+'.*"'} RETURN COUNT(N)`
      neo.cypherQuery(cql, (err, res) => {
        if (err) throw err
        const data = res.data[0]
        resolve(data)
      })
    })
  }
  // 查询节点列表
  const getNodeList = (neo, page, records, keywords, type) => {
    return new Promise(resolve => {
      const cql = `
        MATCH (N${type === 'all' ? '' : ':'+type}) ${keywords === '' ? '' : 'WHERE N.name =~ ".*'+keywords+'.*"'} RETURN N, LABELS(N) SKIP ${(page-1)*records} LIMIT ${records}
      `
      neo.cypherQuery(cql, (err, res) => {
        if (err) throw err
        const data = []
        for (let i = 0; i < res.data.length; i ++) {
          data.push({
            ...res.data[i][0],
            labels: res.data[i][1]
          })
        }
        resolve(data)
      })
    })
  }
  return new Promise(resolve => {
    const neo = new Neo4j(url)
    Promise.all([
      getNodeList(neo, page, records, keywords, type),
      getNodeCount(neo, keywords, type)
    ]).then(res => {
      const data = {
        total: res[1],
        nodeList: res[0]
      }
      resolve(data)
    })
  })
}

// 根据节点id，获取节点的信息以及一层关系网络
// 节点信息：节点名称，节点的label
// 一层关系：前驱节点数组，后继节点数组
const getNodeInfoById = ({ url }, nodeId) => {
  // 获取前驱节点
  const getPrevNodeList = (neo, nodeId) => {
    return new Promise(resolve => {
      const cql = `MATCH (P)-[PR]->(N) WHERE ID(N)=${nodeId} RETURN LABELS(P), P, TYPE(PR), PR LIMIT 10`
      neo.cypherQuery(cql, (err, res) => {
        if (err) throw err
        const data = res.data.map(item => {
          return {
            prevNode: {
              id: item[1]._id,
              name: item[1].name,
              type: item[0]
            },
            relation: {
              id: item[3]._id,
              type: item[2]
            }
          }
        })
        resolve(data)
      })
    })
  }

  // 获取后继节点
  const getBackNodeList = (neo, nodeId) => {
    return new Promise(resolve => {
      const cql = `MATCH (N)-[BR]->(B) WHERE ID(N)=${nodeId} RETURN LABELS(B), B, TYPE(BR), BR LIMIT 10`
      neo.cypherQuery(cql, (err, res) => {
        if (err) throw err
        const data = res.data.map(item => {
          return {
            relation: {
              id: item[3]._id,
              type: item[2]
            },
            backNode: {
              id: item[1]._id,
              name: item[1].name,
              type: item[0]
            }
          }
        })
        resolve(data)
      })
    })
  }

  // 获取节点信息
  const getCurNode = (neo, nodeId) => {
    return new Promise(resolve => {
      const cql = `MATCH (N) WHERE ID(N)=${nodeId} RETURN N, LABELS(N)`
      neo.cypherQuery(cql, (err, res) => {
        if (err) throw err
        const data = {
          id: res.data[0][0]._id,
          name: res.data[0][0].name,
          type: res.data[0][1]
        }
        resolve(data)
      })
    })
  }

  return new Promise(resolve => {
    const neo = new Neo4j(url)
    Promise.all([
      getPrevNodeList(neo, nodeId),
      getBackNodeList(neo, nodeId),
      getCurNode(neo, nodeId)
    ]).then(res => {
      resolve({
        prevNodeList: res[0],
        backNodeList: res[1],
        curNode: res[2]
      })
    })
  })
}

// 根据节点id，修改节点的信息
const updateNodeInfoById = ({ url }, nodeId, nodeName, nodeType, oldNodeType) => {
  return new Promise(resolve => {
    const neo = new Neo4j(url)
    const cql = `MATCH (N) WHERE ID(N)=${nodeId} ${'REMOVE N:' + oldNodeType.join(' REMOVE N:')} SET N.name='${nodeName}' ${'SET N:' + nodeType.join(' SET N:')} RETURN LABELS(N)`
    // console.log(cql)
    neo.cypherQuery(cql, (err, res) => {
      if (err) throw err
      const data = res.data[0]
      resolve({
        name: nodeName,
        type: data
      })
    })
  })
}

// 新增节点
const createNode = ({ url }, nodeName, nodeType) => {
  // 查询nodeName是否有重复的节点 返回重复节点ID 否则 undefined
  const nodeIsExist = (neo, nodeName) => {
    return new Promise(resolve => {
      const cql = `MATCH (N) WHERE N.name='${nodeName}' RETURN ID(N)`
      neo.cypherQuery(cql, (err, res) => {
        if (err) throw err
        resolve(res.data[0])
      })
    })
  }
  // 重复：设置新标签即可
  const existNode = (neo, nodeId, nodeType) => {
    return new Promise(resolve => {
      const cql = `MATCH (N) WHERE ID(N)=${nodeId} ${'SET N:'+nodeType.join(' SET N:')} RETURN ID(N)`
      neo.cypherQuery(cql, (err, res) => {
        if (err) throw err
        resolve(res.data[0])
      })
    })
  }
  // 没有：创建节点即可
  const newNode = (neo, nodeName, nodeType) => {
    return new Promise(resolve => {
      const cql = `CREATE (N:${nodeType.join(':')} { name: '${nodeName}' }) RETURN ID(N)`
      neo.cypherQuery(cql, (err, res) => {
        if (err) throw err
        resolve(res.data[0])
      })
    })
  }
  return new Promise(async resolve => {
    const neo = new Neo4j(url)
    const existNodeId = await nodeIsExist(neo, nodeName)
    if (existNodeId === undefined) {
      // 节点不存在，创建新节点
      const node = await newNode(neo, nodeName, nodeType)
      resolve(node)
    } else {
      // 节点存在
      const node = await existNode(neo, existNodeId, nodeType)
      resolve(node)
    }
  })
}

// 删除节点的关系
const deleteNodeListRelations = ({ url }, ids) => {
  return new Promise(resolve => {
    const neo = new Neo4j(url)
    const cql = `MATCH (N)-[R]-() WHERE ID(N) IN ${ids} DELETE R`
    neo.cypherQuery(cql, (err, res) => {
      if (err) throw err
      console.log(res)
      resolve(res)
    })
  })
}

// 删除节点列表
const deleteNodeList = ({ url }, ids) => {
  // 首先删除节点的所有关系
  const deleteNodeListRelations = (neo, ids) => {
    return new Promise(resolve => {
      const cql = `MATCH (N)-[R]-() WHERE ID(N) IN [${ids.join(', ')}] DELETE R`
      neo.cypherQuery(cql, (err, res) => {
        if (err) throw err
        resolve(res)
      })
    })
  }
  // 删除各个节点
  const deleteNodeList = (neo, ids) => {
    return new Promise(resolve => {
      const cql = `MATCH (N) WHERE ID(N) IN [${ids.join(', ')}] DELETE N`
      neo.cypherQuery(cql, (err, res) => {
        if (err) throw err
        resolve(res)
      })
    })
  }
  return new Promise(async resolve => {
    const neo = new Neo4j(url)
    // 首先删除关系
    await deleteNodeListRelations(neo, ids)
    // 其次删除节点
    await deleteNodeList(neo, ids)
    resolve()
  })
}

module.exports = {
  getNodeListByPage, // 分页查询节点列表
  getNodeInfoById, // 根据节点id获取节点信息
  updateNodeInfoById, // 根据节点id修改节点的信息
  createNode, // 创建节点
  deleteNodeListRelations, // 删除节点列表中节点所有的关系
  deleteNodeList, // 删除节点列表中的所有节点
}